const axios = require('axios')
const ajv = require('ajv')
const payApiUrl = require('./api-url').pay
const uilts = require('./utils')
const https = require('https')
const fs = require('fs')
const path = require('path')

class WechatPay {
  constructor(config = {}) {
    this.config = config
    this.axios = this.createAxios()
  }

  createAxios() {
    const instance = axios.create({})

    instance.interceptors.request.use(
      (config) => {
        config = {
          buildXML: true,
          sign: true,
          ...config,
        }

        config.data = {
          // 商户平台API密钥
          key: this.config.partnerKey,
          ...config.data,
        }

        config.onSuccess = Promise.resolve
        config.onFail = (error, res) => Promise.reject([error, res])

        const { sign, cert } = config
        const { pfx } = config.data

        if (cert && !config.httpsAgent)
          config.httpsAgent = new https.Agent({
            pfx: fs.readFileSync(pfx),
            passphrase: config.data.mch_id,
          })

        if (sign && !config.data.sign) config.data.sign = uilts.sign(config.data)

        if (config.buildXML) config.data = uilts.buildXML(config.data)
        if (config.debug) console.log(config)

        return config
      },
      (error) => {
        return Promise.reject([error])
      }
    )

    instance.interceptors.response.use(
      async (response) => {
        const { config, status } = response

        if (status == 200) {
          const data = await uilts.parseXML(response.data)
          if (data.return_code == 'FAIL') {
            return config.onFail(data.return_msg, data)
          } else if (data.err_code) {
            return config.onFail(data.err_code_des || '数据解析异常', data)
          }

          return data
        } else {
          throw '微信服务器异常'
        }
      },
      (error) => {
        const { config, response } = error

        if (response) {
          return config.onFail('服务器异常', response.data)
        } else {
          if (config) {
            return config.onFail(error.message, error)
          } else {
            return Promise.reject([error.message, error])
          }
        }
      }
    )

    return instance
  }

  /**
   * 创建统一订单
   * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
   * @param {*} options
   * @param {String} options.body 商品描述
   * @param {Number} options.total_fee 标价金额(分)
   * @param {String} [options.out_trade_no] 商户订单
   * @param {String} [options.spbill_create_ip] 终端IP
   * @param {String} [options.appid] 公众账号ID 默认使用全局配置
   * @param {String} [options.mch_id] 商户号 默认使用全局配置
   * @param {String} [options.key] 商户平台API密钥 默认使用全局配置
   * @param {Number} [options.trade_type=JSAPI] 交易类型 JSAPI、JSAPI支付 NATIVE、Native支付 APP、APP支付
   * @param {String} [options.openid] openid trade_type=JSAPI时次参数比传
   * @param {String} [options.notify_url] 通知地址
   * @param {String} [options.attach] 附加数据，在查询API和支付通知中原样返回，可作为自定义参数使用。
   */
  async createUnifiedOrder(options) {
    options = {
      // 公众账号ID
      appid: options.appId || this.config.appId,

      // 商户id
      mch_id: options.mchId || this.config.mchId,

      // 商户平台API密钥
      key: options.partnerKey || this.config.partnerKey,

      // 随机字符串
      nonce_str: uilts.nonceStr(),

      // 商户订单号
      out_trade_no: uilts.outTradeNo(),

      // 终端IP
      spbill_create_ip: '127.0.0.1',

      // 支付回调URL
      notify_url: this.config.notifyUrl,

      // 交易类型 JSAPI、JSAPI支付 NATIVE、Native支付 APP、APP支付
      trade_type: 'JSAPI',

      ...options,
    }

    const partnerKey = options.key

    // options.sign = uilts.sign(options);

    const requiredKey = ['appid', 'mch_id', 'nonce_str', 'body', 'out_trade_no', 'total_fee', 'notify_url', 'trade_type']

    if (options.trade_type == 'JSAPI') requiredKey.push('openid')

    uilts.ajv(options, {
      required: requiredKey,
    })

    return this.axios({
      method: 'POST',
      url: payApiUrl.unifiedOrder,
      data: options,
    }).then(async (data) => {
      if (options.trade_type == 'JSAPI')
        return this.getJsApiParams({
          key: partnerKey,
          appId: options.appid,
          prepay_id: data.prepay_id,
        })

      return data
    })
  }

  /**
   * 获取JSAPI支付参数
   * @param {*} options
   */
  async getJsApiParams(options) {
    uilts.ajv(options, {
      required: ['key', 'appId', 'prepay_id'],
    })

    const params = {
      // 公众号id
      appId: options.appId,

      // 商户号API秘钥
      key: options.key,

      // 时间戳
      timeStamp: uilts.timeStamp().toString(),

      // 随机字符串
      nonceStr: uilts.nonceStr(),

      // 统一下单接口返回的prepay_id参数值
      package: 'prepay_id=' + options.prepay_id,

      // signType
      signType: 'MD5',
    }

    params.paySign = uilts.sign(params)

    return params
  }

  /**
   * 微信订单查询
   * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2
   * @param {*} options
   * @param {*} [options.appid]
   * @param {*} [options.mch_id]
   * @param {*} [options.transaction_id]
   * @param {*} [options.out_trade_no]
   * @param {*} [options.nonce_str]
   */
  orderQuery(options) {
    options = {
      // 公众号id
      appid: options.appId || this.config.appId,

      // 商户id
      mch_id: options.mchId || this.config.mchId,

      // 商户平台API密钥
      key: options.partnerKey || this.config.partnerKey,

      // 随机字符串
      nonce_str: uilts.nonceStr(),

      ...options,
    }

    uilts.ajv(options, {
      required: ['appid', 'mch_id', 'key', 'nonce_str'],
    })

    if ((!options.transaction_id || options.transaction_id == '') && (!options.out_trade_no || options.out_trade_no == '')) {
      throw '微信订单号和商户订单号二选一'
    }

    // if (!options.sign) options.sign = uilts.sign(options);

    return this.axios({
      method: 'POST',
      url: payApiUrl.orderQuery,
      data: options,
    })
  }

  /**
   * 退款申请
   * 微信订单号/商户订单号 二选一
   * https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_4&index=4
   * @param {*} options
   * @param {*} [options.appid] 公众账号ID
   * @param {*} [options.mch_id] 商户号
   * @param {*} [options.nonce_str] 随机字符串
   */
  refund(options) {
    options = {
      // 公众号id
      appid: this.config.appId,

      // 商户id
      mch_id: this.config.mchId,

      // 商户平台API密钥
      key: this.config.partnerKey,

      // 随机字符串
      nonce_str: uilts.nonceStr(),

      pfx: this.config.pfx,

      ...options,
    }

    return this.axios({
      method: 'POST',
      url: 'https://api.mch.weixin.qq.com/secapi/pay/refund',
      data: options,
      cert: true,
      // httpsAgent,
    })
  }
}

module.exports = WechatPay
